package junior.util.config;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import junior.util.string.StringPlaceHolderUtils;

/**
 * 
 * @author bo.wu
 * @CreateDate Oct 27, 2015
 */
public abstract class AbstractConfigContext implements ConfigContext {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private Map<String, String> variables = new HashMap<String, String>();
	
	protected Map<String, String> props;
	
	private Map<String, String> cfgs = new HashMap<String, String>();
	
    private boolean ignoreCase;
    
    private boolean systemOverride;
    
    public boolean isIgnoreCase() {
		return ignoreCase;
	}
	public void setIgnoreCase(boolean ignoreCase) {
		this.ignoreCase = ignoreCase;
	}
    public boolean isSystemOverride() {
		return systemOverride;
	}
	public void setSystemOverride(boolean systemOverride) {
		this.systemOverride = systemOverride;
	}
	
	public void init() {
    	props = loadConfigs();
    }
    
    public void setProperty(String key, String value) {
    	if (beforeSetProperty(key, value)) {
    		
        	props.put(key, value);
        	cfgs.remove(key);
        	
        	afterSetProperty(key, value);
    	}
    }

	@Override
	public void setVariable(String varName, String varValue) {
    	variables.put(varName, varValue);
	}

    public Map<String, String> getValues(String keyPrefix) {
        Map<String, String> resultMap = new HashMap<String, String>();

        for (String key : props.keySet()) {
            if (key.startsWith(keyPrefix)) {
                resultMap.put(key, getProperty(key));
            }
        }
        return resultMap;
    }

    public boolean getBoolean(String key) {
        return getBoolean(key, false);
    }

    public boolean getBoolean(String key, boolean defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Boolean.parseBoolean(value);
        }
        return defaultValue;
    }
	@Override
	public Boolean getBoolean(String key, Boolean defaultValue) {
		String value = getProperty(key);
		if (value != null) {
			return Boolean.valueOf(value);
		}
		return defaultValue;
	}

    public char getChar(String key) {
        return getChar(key, '\u0000');
    }

    public char getChar(String key, char defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return value.charAt(0);
        }
        return defaultValue;
    }

    @Override
	public Character getCharacter(String key, Character defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Character.valueOf(value.charAt(0));
        }
        return defaultValue;
	}

    public double getDouble(String key) {
        return getDouble(key, 0D);
    }

    public double getDouble(String key, double defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Double.parseDouble(value);
        }
        return defaultValue;
    }
	@Override
	public Double getDouble(String key, Double defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Double.valueOf(value);
        }
        return defaultValue;
	}

    public float getFloat(String key) {
        return getFloat(key, 0F);
    }

    public float getFloat(String key, float defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Float.parseFloat(value);
        }
        return defaultValue;
    }
	@Override
	public Float getFloat(String key, Float defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Float.valueOf(value);
        }
        return defaultValue;
	}

    public int getInt(String key) {
        return getInt(key, 0);
    }

    public int getInt(String key, int defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Integer.parseInt(value);
        }
        return defaultValue;
    }
	@Override
	public Integer getInteger(String key, Integer defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Integer.valueOf(value);
        }
        return defaultValue;
	}

    public BigInteger getBigInteger(String key) {
        return getBigInteger(key, null);
    }

    public BigInteger getBigInteger(String key, BigInteger defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return new BigInteger(value);
        }
        return defaultValue;
    }

    public BigDecimal getBigDecimal(String key) {
        return getBigDecimal(key, null);
    }

    public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return new BigDecimal(value);
        }
        return defaultValue;
    }

    public long getLong(String key) {
        return getLong(key, 0L);
    }

    public long getLong(String key, long defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Long.parseLong(value);
        }
        return defaultValue;
    }
	@Override
	public Long getLong(String key, Long defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Long.valueOf(value);
        }
        return defaultValue;
	}

    public short getShort(String key) {
        return getShort(key, (short) 0);
    }

    public short getShort(String key, short defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Short.parseShort(value);
        }
        return defaultValue;
    }
	@Override
	public Short getShort(String key, Short defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return Short.valueOf(value);
        }
        return defaultValue;
	}
	
	public String getString(String key, String defaultValue) {
        String value = getProperty(key);
        if (value != null) {
            return value;
        }
        return defaultValue;
    }

    public String getString(String key) {
        return getProperty(key);
    }
    
    protected boolean beforeSetProperty(String key, String value) {return true;}
    
    protected void afterSetProperty(String key, String value) {}

    protected abstract Map<String , String> loadConfigs();
    
    private String getProperty(String key) {
    	key = ignoreCase ? key.toLowerCase() : key;
    	
    	if (cfgs.containsKey(key)) {
    		return cfgs.get(key);
    	}
    	
    	String value = props.get(key);
    	
    	if (value == null) value = variables.get(key);
    	
    	if (systemOverride) {
    		String _value = System.getProperty(key);
    		if (_value != null) value = _value;
    	}
    	
		if(value != null && value.indexOf("${") != -1) {
			Set<String> placeHolders = StringPlaceHolderUtils.getPlaceHolders(value);
			
			Map<String, String> varMap = new HashMap<String, String>();
			
			for(String placeHolder : placeHolders) {
                if (placeHolders.size() == 1 && value.equals("${"+key+"}")) {
                    // prevent dead circle code
                    return value;
                }
				String placeHolderValue = getProperty(placeHolder);
				
				if(placeHolderValue != null) {
					varMap.put(placeHolder, placeHolderValue);
				}
			}
			
			value =  StringPlaceHolderUtils.replace(value, varMap);
		}
		
		if (props.containsKey(key)) {
			cfgs.put(key, value);
		}
		
		return value;
    }
}
